#pragma once
#include <iostream>
#include <algorithm>
#include "index.hpp"
#include "util.hpp"
#include "log.hpp"
#include <jsoncpp/json/json.h>

namespace ns_searcher{

    struct InvertedElemPrint{
        uint64_t doc_id = 0;
        int weight = 0;
        std::vector<std::string> words;
    };

    class Searcher{
        private:
            ns_index::Index *index;

        public:
            Searcher(){}
            ~Searcher(){}
        public:
            void InitSearcher(const std::string& input)
            {
                index = ns_index::Index::GetInstance();
                LOG(NORMAL, "获取index单例成功...");
                if(!index->BuildIndex(input)){
                    return;
                }
                LOG(NORMAL, "建立正排和倒排索引成功...");
            }

            void Search(const std::string& query, std::string *json_string)
            {
                std::vector<std::string> query_words;
                ns_util::JiebaUtil::CutString(query, &query_words);

                std::vector<InvertedElemPrint> inverted_index_all;
                std::unordered_map<uint64_t, InvertedElemPrint> tokens_map;
                for(auto &word : query_words)
                {
                    boost::to_lower(word);
                    ns_index::InvertedList* inverted_list = index->GetInvertedList(word);
                    if(inverted_list == nullptr){
                        LOG(WARNING, word + " GetInvertedList false!");
                        continue;
                    }
                    for(auto &elm : *inverted_list)
                    {
                        InvertedElemPrint& node =  tokens_map[elm.doc_id];
                        node.doc_id = elm.doc_id;
                        node.weight += elm.weight;
                        node.words.push_back(elm.word);
                    }   
                }
                for(auto &elm : tokens_map)
                    inverted_index_all.push_back(std::move(elm.second));

                std::sort(inverted_index_all.begin(), inverted_index_all.end(), [](InvertedElemPrint& e1, InvertedElemPrint& e2){
                    return e1.weight > e2.weight;
                });

                Json::Value root;
                for(auto &item : inverted_index_all)
                {
                    ns_index::DocInfo* doc = index->GetForwardIndex(item.doc_id);
                    if(doc == nullptr){
                        LOG(WARNING, std::to_string(item.doc_id) + " GetForwardIndex false!");
                        continue;
                    }
                    Json::Value elem;
                    elem["title"] = doc->title;
                    elem["desc"] = GetDesc(doc->content, item.words[0]);
                    elem["url"] = doc->url;
                    elem["doc_id"] = (int)item.doc_id;
                    elem["weight"] = item.weight;
                    root.append(elem);
                }
                Json::StyledWriter writer;
                *json_string = writer.write(root);
            }

            std::string GetDesc(const std::string content, const std::string word)
            {
                const int prev_step = 50;
                const int next_step = 100;
                auto iter = std::search(content.begin(), content.end(), word.begin(), word.end(), [](int c1, int c2){
                    return tolower(c1) == tolower(c2);
                });
                if(iter == content.end()){
                    LOG(WARNING, word + " can not find!");
                    return "None1";
                }
                int pos = std::distance(content.begin(), iter);
                
                int start = 0;
                int end = content.size() - 1;

                if(start + prev_step < pos) start = pos - prev_step;
                if(pos + next_step < end) end = pos + next_step;

                if(start >= end){
                    LOG(WARNING, "运算错误！");
                    return "None2";
                }
                return content.substr(start, end - start + 1);
            }
    };
};